<?php

/*
| +----------------------------------------------------------------------
| | DyCode 框架[为Dy自主开发框架]
| +----------------------------------------------------------------------
| | Copyright (c) 2015-2030 https://www.ngiveup.top/
| +----------------------------------------------------------------------
| | DyCode 提供个人非商业用途免费使用，商业需授权。
| +----------------------------------------------------------------------
| | Author: dy <wdyxzkq@qq.com>
| +----------------------------------------------------------------------
|
*/

declare (strict_types=1);

namespace dy;

use dy\exception\ClassNotFoundException;

/**
 * Class App
 * @package dy
 */
class App extends Container
{
    const VERSION = '1.0.0';

    /**
     * 配置后缀
     * @var string
     */
    protected $configExt = '.php';

    /**
     * 应用调试模式
     * @var bool
     */
    protected $appDebug = true;

    /**
     * 应用开始时间
     * @var float
     */
    protected $beginTime;

    /**
     * 应用内存初始占用
     * @var integer
     */
    protected $beginMem;

    /**
     * 应用类库命名空间
     * @var string
     */
    protected $namespace = 'app';

    /**
     * App constructor.
     */
    public function __construct()
    {
        //初始化框架
        $this->initialize();
    }

    /**
     *  启动入口
     */
    public function run()
    {
        //记录路由和请求信息
        if ($this->appDebug) {
            $this->log('[ ROUTE ] ' . var_export(DY_DISPATCH, true));
            $this->log('[ HEADER ] ' . var_export($this->request->header(), true));
            $this->log('[ PARAM ] ' . var_export($this->request->request(), true));
        }

        //获取对象实例
        $class = DY_CLASS;
        if (class_exists($class)) {
            //halt(DY_CLASS);
            $object = new $class();
        } else {
            throw new ClassNotFoundException('class not exists:' . $class, $class);
        }

        //写入日志
        $this->log->save();

        //运行方法
        $action = DY_ACTION;
        $object->$action();
    }

    /**
     * 初始化框架
     */
    public function initialize(): void
    {
        //设置当前容器的实例
        static::setInstance($this);

        //绑定App类实例到app容器
        $this->instance('app', $this);

        //定义文件路径常量
        $this->defineVar();

        //加载应用文件和配置
        $this->loadConf();

        //应用调试模式
        $this->appDebug = $this->env->get('app_debug', $this->appDebug);
        $this->env->set('app_debug', $this->appDebug);
        if (!$this->appDebug) {
            ini_set('display_errors', 'Off');
        } elseif (PHP_SAPI != 'cli') {
            //重新申请一块比较大的buffer
            if (ob_get_level() > 0) {
                $output = ob_get_clean();
            }
            ob_start();
            if (!empty($output)) {
                echo $output;
            }
        }

        //路由初始化
        $this->routeInit();
    }

    /**
     * 定义文件路径常量
     */
    public function defineVar(): void
    {
        // 定义文件路径的常量信息
        define('DS', DIRECTORY_SEPARATOR); // 目录分隔符，是在一个预定义常量。在linux下解析为 / ,在windows下解析为 \
        define('ROOT_PATH', getcwd() . DS . '..' . DS); // 项目根目录 getcwd:获取当前工作路径根目录
        define('APP_PATH', ROOT_PATH . 'app' . DS);
        define('CONFIG_PATH', ROOT_PATH . 'config' . DS);
        define('PUBLIC_PATH', ROOT_PATH . 'public' . DS);
        define('DOC_PATH', ROOT_PATH . 'doc' . DS);
        define('ROUTER_PATH', ROOT_PATH . 'router' . DS);
        define('RUNTIME_PATH', ROOT_PATH . 'runtime' . DS);
        define('TEMP_PATH', RUNTIME_PATH . 'temp' . DS);

        define('CONTROLLER_PATH', APP_PATH . 'Controller' . DS);
        define('MODEL_PATH', APP_PATH . 'Model' . DS);
        define('SERVICE_PATH', APP_PATH . 'Service' . DS);

        define('RESOURCES_PATH', ROOT_PATH . 'resource' . DS);
        define('VIEW_PATH', RESOURCES_PATH . 'view' . DS);

        define('FRAMEWORK_PATH', ROOT_PATH . 'framework' . DS);
        define('LANG_PATH', FRAMEWORK_PATH . 'lang' . DS);
        define('LIBRARY_PATH', FRAMEWORK_PATH . 'library' . DS);
        define('TPL_PATH', FRAMEWORK_PATH . 'tpl' . DS);
    }

    /**
     * 加载应用文件和配置
     * @access protected
     * @return void
     */
    protected function loadConf(): void
    {
        //框架助手函数
        if (is_file(__DIR__ . '/Helper.php')) {
            include_once __DIR__ . '/Helper.php';
        }

        //用户自定义函数
        if (is_file(__DIR__ . '/../../app/helper.php')) {
            include_once __DIR__ . '/../../app/helper.php';
        }

        // 加载环境变量
        if (is_file(ROOT_PATH . '.env')) {
            $this->env->load(ROOT_PATH . '.env');
        }

        //配置文件
        $files = [];
        if (is_dir(CONFIG_PATH)) {
            $files = glob(CONFIG_PATH . '*' . $this->configExt);
        }
        foreach ($files as $file) {
            if (is_file($file)) {
                $this->config->load($file, pathinfo($file, PATHINFO_FILENAME));
            }
        }
    }

    /**
     * 加载路由
     * @access protected
     * @return void
     */
    protected function routeInit(): void
    {
        // 路由检测
        $files = scandir(ROUTER_PATH);
        foreach ($files as $file) {
            if (strpos($file, '.php')) {
                $filename = ROUTER_PATH . $file;
                // 导入路由配置
                include $filename;
            }
        }

        //初始化请求路径
        $this->request->create($this->request->url(), $this->request->method(), $this->request->param());

        //获取应用调度信息
        $path = $this->request->path();

        //请求类型
        $method = $this->request->method();

        //路由检测
        $dispatch = $this->route->routeCheck($path, $method);

        //路由常量
        define('DY_METHOD', $method);
        define('DY_DISPATCH', $dispatch);
        define('DY_CLASS', $dispatch['class']);
        define('DY_ACTION', $dispatch['action']);
    }

    /**
     * 记录调试信息
     * @access public
     * @param mixed $msg 调试信息
     * @param string $type 信息类型
     * @return void
     */
    public function log($msg, $type = 'info')
    {
        $this->appDebug && $this->log->record($msg, $type);
    }

    /**
     * 获取应用运行时目录
     * @access public
     * @return string
     */
    public function getRuntimePath()
    {
        return RUNTIME_PATH;
    }

    /**
     * 获取配置后缀
     * @access public
     * @return string
     */
    public function getConfigExt(): string
    {
        return $this->configExt;
    }

    /**
     * 获取应用开启时间
     * @access public
     * @return float
     */
    public function getBeginTime()
    {
        return $this->beginTime;
    }

    /**
     * 获取框架版本
     * @access public
     * @return string
     */
    public function version()
    {
        return static::VERSION;
    }

    /**
     * 是否为调试模式
     * @access public
     * @return bool
     */
    public function isDebug()
    {
        return $this->appDebug;
    }

    /**
     * 获取应用初始内存占用
     * @access public
     * @return integer
     */
    public function getBeginMem()
    {
        return $this->beginMem;
    }
}
